#include <windows.h>
#include <fstream>
#include "parser.h"
#include "Regexp.h"
#include "NonFinAut.h"
#include "DetFinAut.h"
#include "RegGrammatica.h"
#include "ProductionRule.h"
#include "RegGrammaticaToNonFinAut.h"
#include "NonFinAutToRegGrammatica.h"
#include "DetFinAutToRegGrammatica.h"
#include "RegexpToNonFinAut.h"
#include "scanner.h"
#include "toDetFinAut.h"
#include "DetFinAutReverse.h"

using namespace std;

typedef enum {CHR, STAR, ALTER, CONCAT, PLUS} node_type;

struct parse_node
{
	parse_node(node_type type_, char data_, parse_node* left_, parse_node* right_)
	    : type(type_), data(data_), left(left_), right(right_) {}
	node_type type;
	char data;
	parse_node* left;
	parse_node* right;	
};

int menu(), hoofdMenu(), regExpSubMenu(), regExpMenu(), regGramSubmenu(), regGramMenu();
int nfaSubMenu(), nfaMenu(), dfaSubMenu(), dfaMenu();
parse_node* expr();
NonFinAut<string> tree_to_nfa(parse_node* tree);

enum fileType{REGGRAMMATICA, NFA, DFA};
enum autType{NFASTR, NFACHAR, DFASTR, DFACHAR};
autType at = NFASTR;

void readFile(char *fileName, fileType type);
void readFileChar(char *fileName, fileType f);
const int NotUsed = system( "Color 81" );   

enum { BLACK = 0, DARK_BLUE = 1, DARK_GREEN = 2, DARK_TEAL = 3, DARK_RED = 4, DARK_PURPLE = 5, DARK_YELLOW = 6, GRAY = 7, DARK_GRAY = 8, BLUE = 9, GREEN = 10, CYAN = 11, RED = 12, PURPLE = 13, YELLOW = 14, WHITE = 15 }; 
void SetColor(const int foreground, const int background) 
{         
	int Color = foreground + (background * 16);     
	HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);     
	SetConsoleTextAttribute(hConsole, Color);     
	return; 
} 
//declaraties
NonFinAut<string> nonFinAut;
NonFinAut<string> nonFinAut1;
DetFinAut<string> detFinAut;
RegGrammatica<string> regGram;
toDetFinAut convert;
DetFinAutReverse reverse;
Scanner my_scanner;
NonFinAut<char> nonFinAutA = NonFinAut<char>();
DetFinAut<char> detFinAutA = DetFinAut<char>();

int main() {
	SetColor(1,8);
	hoofdMenu();
	return 0;
}

int menu( )
{
	int choice;
	cout <<endl;
	cout << "		**********Hoofdmenu Automat 2.0**********" << endl;
	cout << endl;
	cout << "		1 Reguliere Grammatica" << endl;
	cout << "		2 Reguliere Expressie" << endl;
	cout << "		3 NFA" << endl;
	cout << "		4 DFA" << endl;
	cout << "		5 Afsluiten" << endl;
	SetColor(8,8);
	cin >> choice;
	SetColor(1,8);
	return choice;
}

int hoofdMenu()
{
	const int REGGRAM = 1, REGEXP = 2, NFA = 3, DFA = 4, QUIT = 5;
	int choice;
	
	choice = menu( );	
	while ( choice != QUIT )  {
		if ( choice == REGGRAM ) 
		{
			regGramMenu();
		}
		else if ( choice == REGEXP ) 
		{
			regExpMenu();
		}	
		else if (choice == NFA)
		{  
			nfaMenu();
		}
		else if (choice == DFA)
		{
			dfaMenu();
		}
		else
		{
		}

		choice = menu( );
		}
	return 0;
}
//Reguliere expressie submenuitems
int regExpSubMenu()
{
	int choice;
	cout << endl;
	cout << "		**********Reguliere Expressie Menu Automat 2.0**********" << endl;
	cout << endl;
	cout << "		1 Reguliere Expressie invoegen" << endl;
	cout << "		2 Converteren naar NFA" << endl;
	cout << "		3 Transitie Tekening Weergeven" <<endl;
	cout << "		4 Terug" << endl;
	SetColor(8,8);
	cin >> choice;
	SetColor(1,8);
	return choice;
}

int regExpMenu()
{
	set<string> language;
	string expression;
	const int REGEXP = 1, TONFA = 2 , PICTURE = 3, TERUG = 4;
	int choice;
	cout <<endl;
	choice = regExpSubMenu();
	while(choice != TERUG)
	{
		if(choice == REGEXP)
		{
			string regexp;
			cout << "Geef de reguliere expressie: ";
			cin >> regexp;
			
			my_scanner.init(regexp);    
		}
		else if (choice == TONFA)
		{
			parse_node* n = expr();
			NonFinAut<string> nfa = tree_to_nfa(n);
			nonFinAut = nfa;
		}
		else if (choice == PICTURE)
		{
			nonFinAut1 = nonFinAut.show(nonFinAut);
			nonFinAut.makeDrawning(nonFinAut1);
		}
		else if (choice == TERUG)
		{
			hoofdMenu();
		}
		else
		{
		}
		choice = regExpSubMenu();
	}
	return 0;
}
//regGram submenu items
int regGramSubMenu()
{
	int choice;
	cout << endl;
	cout << "		**********Reguliere Grammatica Menu Automat 2.0**********" << endl;
	cout << endl;
	cout << "		1 Reguliere Grammatica inlezen" << endl;
	cout << "		2 Toon de productieregels" << endl;
	cout << "		3 Toon de gegenereerde taal" << endl;
	cout << "		4 Converteren naar NFA" << endl;
	cout << "		5 Transitions na conversie" << endl;
	cout << "		6 Taal na conversie" << endl;
	cout << "		7 Tekening van de NFA" << endl;
	cout << "		8 Terug" << endl;
	SetColor(8,8);
	cin >> choice;
	SetColor(1,8);
	return choice;
}

int regGramMenu()
{
	set<string> language;
	string expression;
	const int REGGRAM = 1, PRODRULES = 2, LANGUAGE = 3,  TONFA = 4 , NFATRANSITIONS = 5, NFALANGUAGE = 6, NFATEKENING = 7, TERUG = 8;
	int choice;
	cout <<endl;
	choice = regGramSubMenu();
	while(choice != TERUG)
	{
		if(choice == REGGRAM)
		{
			char fileName[50];
			cout << "Bestandsnaam : ";
			cin >> fileName;
			readFile(fileName, REGGRAMMATICA);
		}
		else if(choice == PRODRULES)
		{
			SetColor(14, 8);
			regGram.printProdRules();
			SetColor(1, 8);
			
		}
		else if(choice == LANGUAGE)
		{
			int languageLength;
			cout << "Geef lengte: ";
			cin >> languageLength;
			SetColor(14, 8);
			cout <<endl << "Taal van Reguliere Grammatica: ";
			regGram.printLanguage(languageLength);
			SetColor(1, 8);
		}
		else if (choice == TONFA)
		{
			RegGrammaticaToNonFinAut toNonFinAut;
			nonFinAut = toNonFinAut.convertToNonFinAut(regGram);
		}
		else if (choice == NFATRANSITIONS)
		{
			SetColor(14, 8);
			nonFinAut.printTransitionRules();
			SetColor(1, 8);
		}
		else if (choice == NFALANGUAGE)
		{
			int languageLength;
			cout << "Geef lengte: ";
			cin >> languageLength;
			SetColor(14, 8);
			nonFinAut.printLanguage(languageLength);
			SetColor(1, 8);
		}
		else if (choice = NFATEKENING)
		{
			nonFinAut.makeDrawning(nonFinAut);
		}
		else if (choice == TERUG)
		{
			hoofdMenu();
		}
		else
		{
		}
		choice = regGramSubMenu();
	}
	return 0;
}
//Nfa submenu items
int nfaSubMenu()
{
	int choice;
	cout << endl;
	cout << "		**********NFA Menu Automat 2.0**********" << endl;
	cout << endl;
	cout << "		1	NFA inlezen" << endl;
	cout << "		2	Transitions" << endl;
	cout << "		3	Tekening van de NFA" << endl;
	cout << "		4	NFA naar Reguliere Grammatica" <<endl;
	cout << "		5	Print Reguliere Grammatica Productionrules" <<endl;
	cout << "		6	Print Reguliere Grammatica Language" <<endl;
	cout << "		7	NFA converteren naar DFA" <<endl;
	cout << "		8	DFA Minimaliseren" << endl;
	cout << "		9	Accepteerd de taal" << endl;
	cout << "		10	Terug" << endl;
	SetColor(8,8);
	cin >> choice;
	SetColor(1,8);
	return choice;
}

int nfaMenu()
{
	const int MAKENFA = 1, TRANS = 2, TEKENING = 3, TOREGGRAM = 4, PRINT_RG_PRODRULES = 5, PRINT_RG_LANGUAGE = 6, NFATODFA = 7, MINI = 8, ACCEPT = 9, TERUG = 10;
	int choice;
	cout <<endl;
	
	choice = nfaSubMenu();
	while(choice != TERUG)
	{
		
		if(choice == MAKENFA)
		{
			char fileName[50];
			cout << "Bestandsnaam : ";
			cin >> fileName;
			readFile(fileName, NFA);
		}
		else if(choice == TRANS)
		{
			SetColor(14, 8);
			if(at == NFASTR)
			{
				nonFinAut.printTransitionRules();
			}
			if(at == NFACHAR) 
			{
				nonFinAutA.printTransitionRules();
			}
			SetColor(1, 8);
		}
		else if (choice == TEKENING)
		{
			if(at == NFASTR)
			{
				nonFinAut.makeDrawning(nonFinAut);
			}
			if(at == NFACHAR) 
			{
				nonFinAutA.makeDrawning(nonFinAutA);
			}
		}
		else if (choice == TOREGGRAM)
		{
			NonFinAutToRegGrammatica nfaToRegGram;
			regGram = nfaToRegGram.nonFinAutToRegGrammatica(nonFinAut);
		}
		else if (choice == PRINT_RG_PRODRULES)
		{
			SetColor(14, 8);
			regGram.printProdRules();
			SetColor(1, 8);
		}
		else if (choice == PRINT_RG_LANGUAGE)
		{
			int languageLength;
			cout << "Geef lengte: ";
			cin >> languageLength;
			SetColor(14, 8);
			cout <<endl << "Taal van Reguliere Grammatica: ";
			regGram.printLanguage(languageLength);
			SetColor(1, 8);
		}
		else if (choice == NFATODFA)
		{
			nonFinAutA.rewrite();
			detFinAutA = convert.convertToDetFinAut(nonFinAutA);
			//detFinAutA.makeDrawning(detFinAutA);
		}
		else if (choice == MINI)
		{
			DetFinAut<char> rev = reverse.minimalize(detFinAutA);
			rev.makeDrawning(rev);
		}
		else if (choice == ACCEPT)
		{
			string taal;
			cout << "Taal : ";
			cin >> taal;
			cout <<endl;
			SetColor(14, 8);
			if(at == NFASTR)
			{
				if(nonFinAut.accept(taal))
				{
				cout << "string accepted" << endl;
				}
				else 
				{
					cout << "string not accepted" << endl;
				}
			}
			if(at == DFACHAR || at == NFACHAR)
			{
				if(nonFinAutA.accept(taal))
				{
				cout << "string accepted" << endl;
				}
				else 
				{
					cout << "string not accepted" << endl;
				}
			}
			SetColor(1, 8);
		}
		else if (choice == TERUG)
		{
			hoofdMenu();
		}
		else
		{
		}
		choice = nfaSubMenu();
	}
	return 0;
}
//Dfa submu items
int dfaSubMenu()
{
	int choice;
	cout << endl;
	cout << "		**********DFA Menu Automat 2.0**********" << endl;
	cout << endl;
	cout << "		1 DFA inlezen" << endl;
	cout << "		2 Transitions" << endl;
	cout << "		3 DFA Tekening" << endl;
	cout << "		4 DFA naar Reguliere Grammatica" <<endl;
	cout << "		5 Minimalisatie" << endl;
	cout << "		6 Reverse" << endl;
	cout << "		7 Accepteerd de taal" << endl;
	cout << "		8 Terug" << endl;
	SetColor(8,8);
	cin >> choice;
	SetColor(1,8);
	return choice;
}

int dfaMenu()
{
	const int MAKEDFA = 1, TRANS = 2, TEKENING = 3, DFATOREG = 4, MIN = 5, REVERSE = 6, ACCEPT = 7, TERUG = 8;
	int choice;
	cout <<endl;
	
	choice = dfaSubMenu();
	while(choice != TERUG)
	{
		
		if(choice == MAKEDFA)
		{
			char fileName[50];
			cout << "Bestandsnaam : ";
			cin >> fileName;
			readFile(fileName, DFA);
		}
		else if(choice == TRANS)
		{
			SetColor(14, 8);
			if(at == DFASTR)
			{
				detFinAut.printTransitionRules();
			}
			if(at == DFACHAR || at == NFACHAR)
			{
				detFinAutA.printTransitionRules();
			}
			SetColor(1, 8);
		}
		else if (choice == TEKENING)
		{
			if(at == DFASTR)
			{
				detFinAut.makeDrawning(detFinAut);
			}
			if(at == DFACHAR || at == NFACHAR) 
			{
				detFinAutA.makeDrawning(detFinAutA);
			}			
		}
		else if (choice == DFATOREG)
		{
			DetFinAutToRegGrammatica dfaToRegGram;
			regGram = dfaToRegGram.detFinAutToRegGrammatica(detFinAut);
			SetColor(14, 8);
			regGram.printProdRules();
			SetColor(1, 8);
		}
		else if (choice == MIN)
		{
			detFinAutA = reverse.minimalize(detFinAutA);
			SetColor(14, 8);
			detFinAutA.printTransitionRules();
			detFinAutA.print(5);
			SetColor(1, 8);
		}
		else if (choice == REVERSE)
		{
			nonFinAutA = reverse.reverse(detFinAutA);
			nonFinAutA.makeDrawning(nonFinAutA);
			/*
			NonFinAut<char> nonFinAutB;
			toDetFinAut convert1;
			DetFinAutReverse reverse1;
			list<char> startList, finishList;
			startList.push_front('A');
			finishList.push_front('B');
			finishList.push_front('D');
			finishList.push_front('E');
			//finishList.push_front('G');
	
			nonFinAutB = NonFinAut<char>(startList, finishList);
			nonFinAutB.addTransition('A', 'a', 'C');
			nonFinAutB.addTransition('A', 'b', 'D');
			nonFinAutB.addTransition('B', 'b', 'C');
			nonFinAutB.addTransition('B', 'a', 'D');
			nonFinAutB.addTransition('C', 'a', 'A');
			nonFinAutB.addTransition('C', 'b', 'B');
			nonFinAutB.addTransition('D', 'a', 'B');
			nonFinAutB.addTransition('D', 'b', 'E');
			nonFinAutB.addTransition('E', 'a', 'C');
			nonFinAutB.addTransition('E', 'b', 'E');
			//nonFinAutB.addTransition('F', 'a', 'C');
			//nonFinAutB.addTransition('F', 'b', 'A');
			//nonFinAutB.addTransition('G', 'a', 'E');
			//nonFinAutB.addTransition('G', 'b', 'A');
			nonFinAutB.rewrite();
			DetFinAut<char> test = (reverse.minimalize(convert.convertToDetFinAut(nonFinAutB)));
			test.makeDrawning(test);*/
		}
		else if (choice == ACCEPT) 
		{
			string taal;
			cout << "Taal : ";
			cin >> taal;
			SetColor(14, 8);
			cout <<endl;
			if(at == DFASTR)
			{
				if(detFinAut.accept(taal))
				{
				cout << "string accepted" << endl;
				}
				else 
				{
					cout << "string not accepted" << endl;
				}
			}
			if(at == DFACHAR || at == NFACHAR)
			{
				if(detFinAutA.accept(taal))
				{
				cout << "string accepted" << endl;
				}
				else 
				{
					cout << "string not accepted" << endl;
				}
			}
			SetColor(1, 8);
		}
		else if (choice == TERUG)
		{
			hoofdMenu();
		}
		else
		{
		}
		choice = dfaSubMenu();
	}
	return 0;
}
//Functie bestanden inlezen
void readFile(char *fileName, fileType f)
{
	ifstream myfile(fileName);
	if(f == NFA)
	{
		list<string> startList, finishList;
		list<char> startListChar, finishListChar;
		nonFinAut = NonFinAut<string>(startList, finishList);
		nonFinAutA = NonFinAut<char>(startListChar, finishListChar); //add StartState methode
		string line;
	if (myfile.is_open())
	{
		getline(myfile, line);
		string stringOrChar = line.substr(0, 3);
		if(stringOrChar == "str")
		{
			at = NFASTR;
			while (! myfile.eof() )
			{
				while(getline(myfile, line))
				{
					//add StartState methode
					string startState = "";
					string finishState = "";
					string transition = "";
					if(line.length() >9)
					{
						string deel = line.substr(0, 5);
						if(deel == "start")
						{
							startState += string("") + line[10];
							startState += string("") + line[11];
							nonFinAut.addStartState(startState);
						}
						if(deel == "finis")
						{
							finishState += string("") + line[11];
							finishState += string("") + line[12];
							nonFinAut.addFinishState(finishState);
						}	
					}
					else if(line.length() < 9)
					{
						for(unsigned int i = 0; i< line.length(); i++)
						{
							if(line[i] != ':')
							{
								transition +=line[i];
							}
						}
						string left = string("") + transition[0];
						left += transition[1];
						char symbol = transition[2];
						string right = string("") + transition[3];
						right += transition[4];
						nonFinAut.addTransition(left, symbol, right);
						}
					}
						
			}
			cout <<endl << "Bestand inlezen succesvol" << endl;
		}
		else
		{
			at = NFACHAR;
			while (! myfile.eof() )
			{
				while(getline(myfile, line))
				{
					string stringOrChar = line.substr(0, 3);
					char transition[8] = "";
					if(line.length() > 9)
					{
						string deel = line.substr(0, 5);
						if(deel == "start")
						{
							char startState[1];
							startState[0] = line[10];
							nonFinAutA.addStartState(startState[0]);
						}
						if(deel == "finis")
						{
						char finishState[1];
						finishState[0] = line[11];
						nonFinAutA.addFinishState(finishState[0]);
						}	
					}
					else if(line.length() < 9 && line.length() > 3)
					{
						for(unsigned int i = 0; i< line.length(); i++)
						{
							if(line[i] != ':')
							{
								transition[i]=line[i];
							}
						}
						char left[1];
						left[0] = transition[0];
						char symbol[1];
						symbol[0] = transition[2];
						char right[1];
						right[0] = transition[4];
						nonFinAutA.addTransition(left[0], symbol[0], right[0]);
					}
						
				}
				cout <<endl << "Bestand inlezen succesvol" << endl;
			}
		}
	myfile.close();
	}
	else cout << "Kan het bestand niet openen";
	}

	if(f == REGGRAMMATICA)
	{
		list<ProductionRule<string> > productionRules;
		list<char> alphabet;
		list<string> helpSymbols;
		string startSymbol = "";
		string line;
		if (myfile.is_open())
			{
				while (! myfile.eof() )
				{
					while(getline(myfile, line))
					{
						string productionRule = "";
						string nonTerminal = "";
						if(line.length() >9)
						{
							string deel = line.substr(0, 5);
							if(deel == "start")
							{
								startSymbol += string("") + line[12];
								startSymbol += string("") + line[13];
							}
							if(deel == "alpha")
							{
								alphabet.push_back(line[9]);
							}
							if(deel == "nonTe")
							{
								nonTerminal += string("") + line[12];
								nonTerminal += string("") + line[13];
								helpSymbols.push_back(nonTerminal);
							}
						}
						else if(line.length() < 9)
						{
							for(unsigned int i = 0; i< line.length(); i++)
							{
								if(line[i] != ':')
								{
									productionRule +=line[i];
								}
							}
							if(productionRule.length() > 4)
							{
								string left = string("") + productionRule[0];
								left += productionRule[1];
								char symbol = productionRule[2];
								string right = string("") + productionRule[3];
								right += productionRule[4];
								productionRules.push_back(ProductionRule<string>(left, symbol, right));
							}
							if(productionRule.length() < 5)
							{
								string left = string("") + productionRule[0];
								left += productionRule[1];
								char symbol = productionRule[2];
								productionRules.push_back(ProductionRule<string>(left, symbol));
							}
						}
					}
					regGram = *new RegGrammatica<string>(helpSymbols, alphabet, productionRules, startSymbol);
					cout <<endl << "Bestand inlezen succesvol" << endl;
				}
				myfile.close();
				}
				else cout << "Kan het bestand niet openen";
	}
	if(f == DFA)
	{
		at = DFASTR;
		string startState = "";
		list<string> finishList;
		detFinAut = DetFinAut<string>(startState, finishList); //add StartState methode
		list<char> finishListChar;
		char startListChar[1] = "";
		detFinAutA = DetFinAut<char>(startListChar[0], finishListChar);
		//detFinAutA = (startListChar[0], finishListChar); //add StartState methode
		string line;
		if (myfile.is_open())
		{
			getline(myfile, line);
			string stringOrChar = line.substr(0, 3);
			if(stringOrChar == "str")
			{
				while (! myfile.eof() )
				{
					while(getline(myfile, line))
					{
						string finishState = "";
						string transition = "";
						if(line.length() >9)
						{
							string deel = line.substr(0, 5);
							if(deel == "start")
							{
								startState += string("") + line[11];
								startState += string("") + line[12];
								detFinAut.setStart(startState);
							}
							if(deel == "finis")
							{
								finishState += string("") + line[11];
								finishState += string("") + line[12];
								detFinAut.addFinishState(finishState);
							}	
						}
						else if(line.length() < 9){
						for(unsigned int i = 0; i< line.length(); i++)
							{
								if(line[i] != ':')
								{
									transition +=line[i];
								}
							}
						string left = string("") + transition[0];
						left += transition[1];
						char symbol = transition[2];
						string right = string("") + transition[3];
						right += transition[4];
						detFinAut.addTransition(left, symbol, right);
						}
					}
					cout <<endl << "Bestand inlezen succesvol" << endl;
				}
			}
			else
			{
				at = DFACHAR;
			while (! myfile.eof() )
			{
				while(getline(myfile, line))
				{
					string stringOrChar = line.substr(0, 3);
					char transition[8] = "";
					if(line.length() > 9)
					{
						string deel = line.substr(0, 5);
						if(deel == "start")
						{
							char startState[1];
							startState[0] = line[10];
							detFinAutA.setStart(startState[0]);
						}
						if(deel == "finis")
						{
						char finishState[1];
						finishState[0] = line[11];
						detFinAutA.addFinishState(finishState[0]);
						}	
					}
					else if(line.length() < 9 && line.length() > 3)
					{
						for(unsigned int i = 0; i< line.length(); i++)
						{
							if(line[i] != ':')
							{
								transition[i]=line[i];
							}
						}
						char left[1];
						left[0] = transition[0];
						char symbol[1];
						symbol[0] = transition[2];
						char right[1];
						right[0] = transition[4];
						detFinAutA.addTransition(left[0], symbol[0], right[0]);
					}
						
				}
				cout <<endl << "Bestand inlezen succesvol" << endl;
			}
		}
				myfile.close();
	}
				else cout << "Kan het bestand niet openen";
	}
}

parse_node* expr(void);


void print_tree(parse_node* node, unsigned offset)
{
    if (!node)
		return;

    for (unsigned i = 0; i < offset; ++i)
		cout << " ";

    switch (node->type)
    {
	case CHR:
	    cout << node->data;
	    break;
	case ALTER:
	    cout << '|';
	    break;
	case CONCAT:
	    cout << '.';
	    break;
	case STAR:
	    cout << '*';
	    break;
	case PLUS:
	    cout << '+';
	    break;
	default:
	    break;
    }

    cout << endl;

    print_tree(node->left, offset + 4);
    print_tree(node->right, offset + 4);
}


NonFinAut<string> tree_to_nfa(parse_node* tree)
{
	NonFinAut<string> test;
	assert(tree);
	RegexpToNonFinAut<string> regexpToNonfinAut;
	switch (tree->type)
	{
	case CHR:
		return regexpToNonfinAut.build_nfa_basic(tree->data);
	case ALTER:
		return regexpToNonfinAut.build_nfa_alter(tree_to_nfa(tree->left), tree_to_nfa(tree->right));
	case CONCAT:
		return regexpToNonfinAut.build_nfa_concat(tree_to_nfa(tree->left), tree_to_nfa(tree->right));
	case STAR:
		return regexpToNonfinAut.build_nfa_star(tree_to_nfa(tree->left));
	case PLUS:
		return regexpToNonfinAut.build_nfa_plus(tree_to_nfa(tree->left));
	default:
		break;
	}
	return test;
}

parse_node* chr()
{
    char data = my_scanner.peek();
    if (isalnum(data) || data == 0)
    {
		return new parse_node(CHR, my_scanner.pop(), 0, 0);
    }

    cerr 	<< "Parse error: expected alphanumeric, got " 
			<<  my_scanner.peek() << " at #" << my_scanner.get_pos() << endl;
    exit(1);
}

parse_node* atom()
{
    parse_node* atom_node;
    
    if (my_scanner.peek() == '(')
	{
		my_scanner.pop();
		atom_node = expr();
	
		if (my_scanner.pop() != ')')
		{
			cerr << "Parse error: expected ')'" << endl;
			exit(1);	    
		}
		}
		else
		{
			atom_node = chr();
		}	

    return atom_node;
}

parse_node* rep()
{
    parse_node* atom_node = atom();

    if (my_scanner.peek() == '*')
    {
		my_scanner.pop();
		//if(my_scanner().peek() == '*')
		//{
		parse_node* rep_node = new parse_node(STAR, 0, atom_node, 0);
		return rep_node;
    }
	else if(my_scanner.peek() == '+')
	{
		my_scanner.pop();
		
		parse_node* rep_node = new parse_node(PLUS, 0, atom_node, 0);
		return rep_node;
	}
    else
    {
		return atom_node;
    }
}

parse_node* concat()
{
    parse_node* left = rep();

    if (my_scanner.peek() == '.')
    {
		my_scanner.pop();
		parse_node* right = concat();

		parse_node* concat_node = new parse_node(CONCAT, 0, left, right);
		return concat_node;
    }
    else
    {
		return left;
    }
}

parse_node* plus()
{
    parse_node* atom_node = atom();

    if (my_scanner.peek() == '+')
    {
		my_scanner.pop();

		parse_node* plus_node = new parse_node(PLUS, 0, atom_node, 0);
		return plus_node;
    }
    else
    {
		return atom_node;
    }
}

parse_node* expr()
{
    parse_node* left = concat();
    
    if (my_scanner.peek() == '|')
    {
		my_scanner.pop();
		parse_node* right = expr();

		parse_node* expr_node = new parse_node(ALTER, 0, left, right);
		return expr_node;
    }
    else
    {
		return left;
    }
}